package com.atguigu.srb.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.srb.base.asserts.Asserts;
import com.atguigu.srb.base.response.ResponseEnum;
import com.atguigu.srb.core.hfb.FormHelper;
import com.atguigu.srb.core.hfb.HfbConst;
import com.atguigu.srb.core.hfb.LendNoUtils;
import com.atguigu.srb.core.hfb.RequestHelper;
import com.atguigu.srb.core.pojo.entity.Lend;
import com.atguigu.srb.core.pojo.entity.LendItem;
import com.atguigu.srb.core.mapper.LendItemMapper;
import com.atguigu.srb.core.pojo.entity.TransFlow;
import com.atguigu.srb.core.pojo.entity.UserInfo;
import com.atguigu.srb.core.service.*;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.annotation.RequestHeader;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 * 标的出借记录表 服务实现类
 * </p>
 *
 * @author Atguigu
 * @since 2023-05-26
 */
@Service
@Slf4j
public class LendItemServiceImpl extends ServiceImpl<LendItemMapper, LendItem> implements LendItemService {
    /**
     投资：
     前端点击立即投资

     后端：
     需要的参数：登录的token、投资金额、标的id
     > 1、校验账户安全
     > 2、校验账户余额
     > 3、校验标的状态 和 标的金额是否满标
     > 4、保存投资记录
     lend_item
     lend: 已投金额、投资人数
     > 5、锁定投资人的投资金额
     锁定hfb中该用户的账户余额
     更新用户srb账户表中冻结金额和可用余额

     保存交易日志

     投资也需要访问hfb的冻结会员账户金额的接口 、还需要接收回调的参数更新srb会员的账户
     **/
    @Resource
    UserInfoService userInfoService;
    @Resource
    UserAccountService userAccountService;
    @Resource
    LendService lendService;
    @Resource
    TransFlowService transFlowService;
    @Override
    public String commitInvest(String token, BigDecimal amount, String lendId) {

        UserInfo userInfo = userInfoService.checkAccount(token);
        //查询会员的账户可用余额
        BigDecimal accountAmount = userAccountService.getAmount(token);
        // 判断 如果投资金额高于可用余额，报错
        Asserts.assertNotTrue(accountAmount.compareTo(amount)<0 , ResponseEnum.NOT_SUFFICIENT_FUNDS_ERROR);
        // 标的校验：
        Lend lend = lendService.getById(lendId);
        Asserts.assertNotTrue(lend.getStatus().intValue()!=1 , ResponseEnum.LEND_INVEST_ERROR);
        Asserts.assertNotTrue(lend.getAmount()
                .compareTo(lend.getInvestAmount().add(amount)) == -1 , ResponseEnum.LEND_FULL_SCALE_ERROR);
        //投资人 也是本次请求发起的会员
       // UserInfo userInfo = userInfoService.getById(userId);

        // 保存一个待支付的 投资记录
        LendItem lendItem = new LendItem();
        //设置需要的参数
        //投资记录的编号：
        String lendItemNo = LendNoUtils.getLendItemNo();
        lendItem.setLendItemNo(lendItemNo);
        lendItem.setLendId(lend.getId());
        //投资人会员id
        lendItem.setInvestUserId(userInfo.getId());
        lendItem.setInvestName(userInfo.getName());
        lendItem.setInvestAmount(amount);
        lendItem.setLendYearRate(lend.getLendYearRate());
        lendItem.setInvestTime(new Date());
        lendItem.setLendStartDate(lend.getLendStartDate());
        lendItem.setLendEndDate(lend.getLendEndDate());
        //计算收益：
        lendItem.setExpectAmount(userAccountService.getInterestCount(amount,lend.getPeriod(),lend.getLendYearRate(),
                lend.getReturnMethod()));
        lendItem.setRealAmount(new BigDecimal(0));
        lendItem.setStatus(0);
        this.save(lendItem);
        //交易日志
        transFlowService.extracted(amount,userInfo.getId(),userInfo,lendItemNo,"投资");
        //构建自动提交的表单字符串
        Map<String, Object> params = new HashMap<>();
        params.put("agentId",HfbConst.AGENT_ID);
        //投资人 hfb的bindCode
        params.put("voteBindCode",userInfo.getBindCode());
        //借款人 会员数据
        UserInfo benefitUserInfo = userInfoService.getById(lend.getUserId());
        //借款人 hfb的bindCode
        params.put("benefitBindCode",benefitUserInfo.getBindCode());
        // 项目编号：本次投资属于哪个标的
        params.put("agentProjectCode",lend.getLendNo());
        params.put("agentProjectName",lend.getTitle());
        // 本次投资唯一的编号：
        params.put("agentBillNo",lendItemNo);
        params.put("voteAmt",amount);
        params.put("votePrizeAmt",new BigDecimal(0));
        params.put("voteFeeAmt",new BigDecimal(0));
        //标的总金额：
        params.put("projectAmt",lend.getAmount());
        params.put("notifyUrl",HfbConst.INVEST_NOTIFY_URL);
        params.put("returnUrl",HfbConst.INVEST_RETURN_URL);
        params.put("timestamp",System.currentTimeMillis());
        params.put("sign", RequestHelper.getSign(params));
        return FormHelper.buildForm(HfbConst.INVEST_URL,params);
    }

    //回调接口->业务  验证签名、resultCode、投资金额、保证幂等性   投资成功 更新userAccount表用户账户余额、增加冻结金额 ， 更新投资记录的状态，交易日志的状态
    @Override
    public String investNotify(HttpServletRequest request) {
        Map<String, Object> map = RequestHelper.switchMap(request.getParameterMap());
        if(!RequestHelper.isSignEquals(map)){
            log.error("投资回调,验签失败:{}" , JSON.toJSONString(map));
            return "fail";
        }

        if(!StringUtils.equals("0001",map.get("resultCode").toString())){
            log.error("投资回调,业务失败:{}" , JSON.toJSONString(map));
            return "fail";
        }
        //查询交易日志: agentBillNo 投资编号
        String agentBillNo = map.get("agentBillNo").toString();
        TransFlow transFlow = transFlowService.getTransFlowByAgentBillNo(agentBillNo);
        //幂等性判断： 可以结合数据库表中的数据或者redis中缓存的数据、session缓存
        if(transFlow.getStatus()==1){
            return "success";
        }
        BigDecimal transAmount = transFlow.getTransAmount();
        if(transAmount.compareTo(new BigDecimal(map.get("voteAmt").toString()))!=0){
            log.error("投资回调,投资金额错误:{}" , JSON.toJSONString(map));
            return "fail";
        }
        //投资成功：
        // 更新投资记录的状态
        this.update(Wrappers.lambdaUpdate(LendItem.class)
                .eq(LendItem::getLendItemNo,agentBillNo)
                .set(LendItem::getStatus,1)
                .set(LendItem::getUpdateTime,new Date()));
        // 更新交易日志的状态
        transFlow.setStatus(1);
        transFlow.setMemo(JSON.toJSONString(map));
        transFlowService.updateById(transFlow);
        // 更新用户账户余额 + 冻结金额
        //  投资成功但是标的状态仍然是募资中状态，账户余额扣除投资金额转到冻结金额中，直到标的放款时才将冻结金额转入到借款人账户中
        userAccountService.updateInvestAmountByUserId(transAmount,transFlow.getUserId());
        //更新标的的投资人数和投资金额
        Lend lend = lendService.getOne(Wrappers.lambdaQuery(Lend.class)
                .eq(Lend::getLendNo, map.get("agentProjectCode").toString()));
        lend.setInvestNum(lend.getInvestNum()+1);
        lend.setInvestAmount(lend.getInvestAmount().add(transAmount));
        lendService.updateById(lend);
        return "success";
    }

    /**
     * 1、前端提交的请求：
     *      目的是干什么
     * 2、设计后端接口：
     *      根据需求分析前端需要的数据
     *      后端判断是否需要封装自定义bean：建立数据模型
     *      根据需要查询的数据判断前端需要提交哪些参数：
     *
     *      写业务：
     *          判断是否需要多表联查
     *          多表联查分析是否需要自定义sql
     * @param lendId
     * @return
     */
    @Override
    public List<LendItem> listByLendId(String lendId) {
        // 查询投资成功的投资记录
        return this.list(Wrappers.lambdaQuery(LendItem.class)
                .eq(LendItem::getLendId,lendId)
                .in(LendItem::getStatus,1,2));
    }
}
